#include "xethernet.h"
#include "xarp.h"


void xarp_init()
{
    xarp_make_request(&my_ip);

    arp_entry.state = XARP_ENTRY_FREE;
    xnet_check_tmo(&arp_timer, 0);//初始化arp_timer
};

xnet_err_t xarp_make_request(const xipaddr_t *ipaddr)
{
    // 广播，询问所有设备，参数的ipaddr的mac地址
    xnet_packet_t *packet = xnet_alloc_for_send(sizeof(xarp_hdr_t));
    xarp_hdr_t *arp_hdr = (xarp_hdr_t *)packet->data;

    arp_hdr->hw_type = swap_order16(XARP_HW_ETHER);
    arp_hdr->pro_type = swap_order16(XNET_PROTOCOL_IP);
    arp_hdr->hw_len = XNET_MAC_ADDR_SIZE;
    arp_hdr->pro_len = XNET_IPV4_ADDR_SIZE;

    arp_hdr->opcode = swap_order16(XARP_REQUEST);

    memcpy(arp_hdr->send_mac, my_mac, XNET_MAC_ADDR_SIZE);
    memcpy(arp_hdr->sender_ip, my_ip.array, XNET_IPV4_ADDR_SIZE);
    memset(arp_hdr->target_mac, 0, XNET_MAC_ADDR_SIZE);
    memcpy(arp_hdr->target_ip, ipaddr->array, XNET_IPV4_ADDR_SIZE);

    if ((GDB_INFO_SWITCH) && (ARP_GDB_INFO))
    {
        char my_ip_str[50];
        ip_to_str(my_ip.array, my_ip_str,XNET_IPV4_ADDR_SIZE);
        // printf("%s\n",my_ip_str);
        print_info("try to send a arp packet broadcast target = %s", my_ip_str);
    }


    return ethernet_out_to(XNET_PROTOCOL_ARP, ether_broadcast, packet);
};

xnet_err_t xarp_make_reponse(const uint8_t * target_ip,const uint8_t *target_mac){
    // 收到了arp_request 之后 ，回应请求
    xnet_packet_t *packet = xnet_alloc_for_send(sizeof(xarp_hdr_t));
    xarp_hdr_t *arp_hdr = (xarp_hdr_t *)packet->data;

    arp_hdr->hw_type = swap_order16(XARP_HW_ETHER);
    arp_hdr->pro_type = swap_order16(XNET_PROTOCOL_IP);
    arp_hdr->hw_len = XNET_MAC_ADDR_SIZE;
    arp_hdr->pro_len = XNET_IPV4_ADDR_SIZE;

    arp_hdr->opcode = swap_order16(XARP_REPLY);

    memcpy(arp_hdr->send_mac, my_mac, XNET_MAC_ADDR_SIZE);
    memcpy(arp_hdr->sender_ip, my_ip.array, XNET_IPV4_ADDR_SIZE);
    memcpy(arp_hdr->target_mac, target_mac, XNET_MAC_ADDR_SIZE);
    memcpy(arp_hdr->target_ip, target_ip, XNET_IPV4_ADDR_SIZE);

    if (NEED_ARP_GDB)
    {
        char my_ip_str[32];
        ip_to_str(target_ip, my_ip_str,XNET_IPV4_ADDR_SIZE);
        // printf("%s\n",my_ip_str);
        print_info("try to send a arp reponse packet  target = %s", my_ip_str);
    }


    return ethernet_out_to(XNET_PROTOCOL_ARP, target_mac, packet);

};


void xarp_in(xnet_packet_t *packet){

    if(packet->size < sizeof(xarp_hdr_t)){
        return;
    }
    xarp_hdr_t *arp_packet=(xarp_hdr_t *)packet->data;
    uint16_t opcode = swap_order16(arp_packet->opcode);

    	//现在仅支持以太网
	if ((swap_order16(arp_packet->hw_type) != XARP_HW_ETHER) || 
		(arp_packet->hw_len != XNET_MAC_ADDR_SIZE) ||
		(swap_order16(arp_packet->pro_type) != XNET_PROTOCOL_IP) ||
		(arp_packet->pro_len != XNET_IPV4_ADDR_SIZE) ||
		(opcode != XARP_REQUEST && opcode != XARP_REPLY)) {
		/*printf("%d  %d %d %d %d\n", (swap_order16(arp_packet->hw_type) != XARP_HW_ETHER), (arp_packet->hw_len != XNET_MAC_ADDR_SIZE),
			(swap_order16(arp_packet->pro_type) != XNET_PROTOCOL_IP), (arp_packet->pro_len != XNET_IPV4_ADDR_SIZE),
			(opcode != XARP_REQUEST && opcode != XARP_REPLY));*/
		//printf("%d\n", (swap_order16(arp_packet->pro_type) != XNET_PROTOCOL_IP));
		return;
	}
    //不是发给我的包
    if(!xipaddr_is_equal_buf(my_ip,arp_packet->target_ip)){
        return;
    }

    switch (opcode)
    {
    case XARP_REQUEST:
        xarp_make_reponse(arp_packet->sender_ip,arp_packet->send_mac);
        update_arp_entry(arp_packet->sender_ip,arp_packet->send_mac);
        break;
    case XARP_REPLY:
        update_arp_entry(arp_packet->sender_ip,arp_packet->send_mac);
    default:
        break;
    }

};

void update_arp_entry(uint8_t *src_ip, uint8_t *mac_addr)
{
    // 更新arp表单
    memcpy(arp_entry.ipaddr.array, src_ip, XNET_IPV4_ADDR_SIZE);
    memcpy(arp_entry.macaddr, mac_addr, XNET_MAC_ADDR_SIZE);
    arp_entry.retry_cnt = XARP_CFG_MAX_RETRIES;
    arp_entry.state = XARP_ENTRY_OK;
    arp_entry.tmo = XARP_CFG_ENTRY_OK_TMO;

    if(NEED_ARP_GDB){
        char ip_str[32],mac_str[32];
        ip_to_str(src_ip, ip_str,XNET_IPV4_ADDR_SIZE);
        ip_to_str(mac_addr, mac_str,XNET_MAC_ADDR_SIZE);
        print_info("update arp entry src_ip = %s src_mac=%s",ip_str,mac_str);
    }
};




void xarp_poll(){
    //隔一段时间发送广播
    if(xnet_check_tmo(&arp_timer,XARP_TIMER_PERIOD)){
        switch (arp_entry.state)
        {
        case XARP_ENTRY_OK:
            if(--arp_entry.tmo==0){
                xarp_make_request(&arp_entry.ipaddr);
                arp_entry.state=XARP_ENTRY_PENDING;
                arp_entry.tmo=XARP_CFG_ENTRY_PENDING_TMO;
            }
            break;
        case XARP_ENTRY_PENDING:
            if(--arp_entry.tmo==0){
                if(--arp_entry.retry_cnt==0){
                    arp_entry.state=XARP_ENTRY_FREE;//在重试次数内，未发现设备
                }
                else{
                    xarp_make_request(&arp_entry.ipaddr);
                    arp_entry.tmo=XARP_CFG_ENTRY_PENDING_TMO;
                }
            }
            break;
        default:
            break;
        }
    }
    
};

xnet_err_t xarp_resolve(const xipaddr_t *ipaddr,uint8_t **mac_addr){
    //在arp表单中找ipaddr对应的mac地址
    //如果找到了，返回xnet_err_ok
    //没找到，请求，返回xnet_err_none
    if((arp_entry.state==XARP_ENTRY_OK)&&xipaddr_is_equal(*ipaddr,&my_ip)){
        *mac_addr = arp_entry.macaddr;
        if(NEED_ARP_GDB){
            char ip_str[32],mac_str[32];
            ip_to_str(ipaddr->array, ip_str,XNET_IPV4_ADDR_SIZE);
            ip_to_str(mac_addr, mac_str,XNET_MAC_ADDR_SIZE);
            print_info("found ip mapping ip = %s mac=%s",ip_str,mac_str);
        }
       
        return XNET_ERR_OK;
    }   
    else{
        if(NEED_ARP_GDB){
            char ip_str[32],mac_str[32];
            ip_to_str(ipaddr->array, ip_str,XNET_IPV4_ADDR_SIZE);
            print_info("not found ip mapping ip = %s ",ip_str);
        }
        xarp_make_request(ipaddr);
        return XNET_ERR_NONE;
    }
};